use regex::Regex;

pub struct VueBlock {
    source_code: String,
}

impl VueBlock {
    pub fn new(vue_source_code: &str) -> Self {
        Self {
            source_code: vue_source_code.to_string(),
        }
    }

    pub fn get_source(&self) -> &str {
        &self.source_code
    }

    pub fn try_get_script_block(&self) -> Option<String> {
        let re = Regex::new(r"(?s)<script[^>]*>(.*?)</script>").expect("构建script正则");

        if let Some(captures) = re.captures(&self.source_code) {
            return Some(captures[1].to_string());
        }

        None
    }

    pub fn update_script_block(
        &self,
        script_block: &str,
    ) -> Result<Self, Box<dyn std::error::Error>> {
        let re = Regex::new(r"(?s)(<script[^>]*>)(.*?)(</script>)").expect("构建script正则");

        let new_source_code = re.replace(&self.source_code, |caps: &regex::Captures| {
            format!("{}\n{}\n{}", &caps[1], script_block, &caps[3])
        });

        Ok(Self {
            source_code: new_source_code.to_string(),
        })
    }

    pub fn update_template_block(
        &self,
        template_block: &str,
    ) -> Result<Self, Box<dyn std::error::Error>> {
        let re = Regex::new(r#"(?s)<template>.*?</template>"#)?;

        let updated_source_code = re.replace(
            &self.source_code,
            &format!("<template>\n{}\n</template>", template_block),
        );

        Ok(VueBlock {
            source_code: updated_source_code.to_string(),
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_try_get_script_block() {
        let source_code = r#"<template>
  <a-config-provider :locale="locale" :theme="customTheme">
    <router-view></router-view>
  </a-config-provider>
</template>

<script lang="ts" setup>
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
</script>
"#;
        let vue_block = VueBlock::new(source_code);
        let script_block = vue_block.try_get_script_block();

        let expected = Some(
            r#"
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
"#
            .to_string(),
        );

        assert_eq!(script_block, expected);
    }

    #[test]
    fn test_update_script_block() {
        let source_code = r#"<template>
  <a-config-provider :locale="locale" :theme="customTheme">
    <router-view></router-view>
  </a-config-provider>
</template>

<script lang="ts" setup>
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
</script>
"#;

        let vue_block = VueBlock::new(source_code);

        let expected = r#"<template>
  <a-config-provider :locale="locale" :theme="customTheme">
    <router-view></router-view>
  </a-config-provider>
</template>

<script lang="ts" setup>
import { TodoList } from 'todolist_list'
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
</script>
"#;
        let updated_vue_block = vue_block
            .update_script_block(
                r#"import { TodoList } from 'todolist_list'
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}"#,
            )
            .expect("update vue block");
        assert_eq!(updated_vue_block.get_source(), expected);
    }

    #[test]
    fn test_update_template_block() {
        let source_code = r#"<template>
  <a-config-provider :locale="locale" :theme="customTheme">
    <router-view></router-view>
  </a-config-provider>
</template>

<script lang="ts" setup>
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
</script>
"#;

        let vue_block = VueBlock::new(source_code);

        let expected = r#"<template>
<Base>
  <template v-slot="content">
    <TodoList />
  </template>
</Base>
</template>

<script lang="ts" setup>
import zhCN from 'ant-design-vue/es/locale/zh_CN'

const locale = zhCN
const customTheme= {
  token: {}
}
</script>
"#;
        let updated_vue_block = vue_block
            .update_template_block(
                r#"<Base>
  <template v-slot="content">
    <TodoList />
  </template>
</Base>"#,
            )
            .expect("update vue block");
        assert_eq!(updated_vue_block.get_source(), expected);
    }
}
